package top.lingkang.finalgateway.admin.controller;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.thread.ThreadUtil;
import lombok.extern.slf4j.Slf4j;
import org.noear.snack.ONode;
import org.noear.solon.annotation.Controller;
import org.noear.solon.annotation.Inject;
import org.noear.solon.annotation.Mapping;
import org.noear.solon.core.handle.Context;
import org.noear.solon.core.handle.MethodType;
import top.lingkang.finalgateway.FinalGateway;
import top.lingkang.finalgateway.admin.constants.LogType;
import top.lingkang.finalgateway.admin.dto.ResponseResult;
import top.lingkang.finalgateway.admin.entity.AccessLogEntity;
import top.lingkang.finalgateway.admin.mapper.AccessLogMapper;
import top.lingkang.finalgateway.admin.mapper.ActionLogMapper;
import top.lingkang.finalgateway.admin.terminal.TerminalHandler;
import top.lingkang.finalgateway.admin.terminal.WsSimpleListener;
import top.lingkang.finalgateway.utils.CommonUtils;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author lingkang
 * @create by 2024/4/28 16:57
 */
@Slf4j
@Controller
@Mapping("/api")
public class ApiController {
    @Inject("${admin.password}")
    private String password;
    @Inject
    private ActionLogMapper actionLogMapper;
    @Inject
    private AccessLogMapper accessLogMapper;


    @Mapping(value = "/refreshMonitor", method = MethodType.POST)
    public Object refreshMonitor() {
        FinalGateway.refreshMonitor();
        ThreadUtil.sleep(2000);
        return new ResponseResult<>().ok();
    }

    @Mapping(value = "/restartService", method = MethodType.POST)
    public Object restartService(String password, Integer port) {
        Assert.notBlank(password, "密码不能为空");
        Assert.isTrue(
                password.equals(this.password),
                "密码错误");
        if (port != null) {
            Assert.isTrue(FinalGateway.service.containsKey(port), "端口服务不存在: " + port);
            FinalGateway.restartService(port);
            actionLogMapper.addLog(LogType.service_restartPort, "重启服务端口: " + port);
        } else {
            FinalGateway.restartAllService();
            actionLogMapper.addLog(LogType.service_restartAll, "重启全部服务");
        }
        ThreadUtil.sleep(2000);
        return new ResponseResult<>().ok();
    }

    @Mapping(value = "/clearCache", method = MethodType.POST)
    public Object clearCache(Integer port) {
        FinalGateway.clearHttpHandlerCache(port);
        FinalGateway.clearIpListCache(port);
        return new ResponseResult<>().ok();
    }

    @Mapping(value = "/close", method = MethodType.POST)
    public synchronized Object close(String password) {
        Assert.notBlank(password, "密码不能为空");
        Assert.isTrue(password.equals(this.password), "密码错误");
        new Thread(() -> {
            // 刷新监控
            FinalGateway.refreshMonitor();
            ThreadUtil.sleep(1000);
            // 停止netty
            FinalGateway.shutdownAllService();
            ThreadUtil.sleep(1000);
            // 日志记录
            actionLogMapper.addLog(LogType.app_stop, "停止应用");
            log.info("关闭服务完成");
            System.exit(0);
        }).start();
        ThreadUtil.sleep(1000);
        return new ResponseResult<>().ok();
    }

    @Mapping(value = "/accessLogExport", method = MethodType.POST)
    public Object accessLogExport(Integer number) {
        Assert.notNull(number, "归档数据不能为空");
        Assert.isTrue(number > 0, "归档数据不能小于 1");
        long count = accessLogMapper.selectCount();
        if (number > count) {
            number = Math.toIntExact(count);
        }
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss-SSS");
        File file = new File(FinalGateway.dir + File.separator + "conf" + File.separator + "export" + File.separator +
                "access_log-" + format.format(new Date()) + ".json");
        if (!file.getParentFile().exists())
            file.getParentFile().mkdirs();
        if (number <= 100) {
            List<AccessLogEntity> list = accessLogMapper.getLastLine(100);
            List<String> ids = new ArrayList<>(100);
            list.forEach(e -> ids.add(e.getId()));
            FileUtil.writeUtf8String(ONode.load(list).toJson(), file);
            accessLogMapper.deleteIn(ids);
        } else {
            List<AccessLogEntity> list = new ArrayList<>(number);
            int total = number % 100 + 1;
            for (int i = 0; i < total; i++) {
                List<AccessLogEntity> temp = accessLogMapper.getLastLine(100);
                if (temp.isEmpty())
                    break;
                list.addAll(temp);
                List<String> ids = new ArrayList<>(100);
                temp.forEach(e -> ids.add(e.getId()));
                accessLogMapper.deleteIn(ids);
            }
            FileUtil.writeUtf8String(ONode.load(list).toJson(), file);
        }
        return new ResponseResult<>().ok();
    }

    @Mapping(value = "/wsToken", method = MethodType.GET)
    public Object wsToken(Context context) {
        return new ResponseResult<>().setData(context.sessionId());
    }

    @Mapping(value = "/console", method = MethodType.GET)
    public Object console() {
        ONode node = new ONode();
        // 内存情况
        Runtime runtime = Runtime.getRuntime();
        node.set("maxMemory", runtime.maxMemory());
        node.set("totalMemory", runtime.totalMemory());
        // node.set("usedMemory", runtime.totalMemory() - runtime.freeMemory());
        node.set("freeMemory", runtime.freeMemory());

        // 路由
        node.set("router", FinalGateway.config.getRouterConfigs().size());
        node.set("terminal", TerminalHandler.threadPool.getPoolSize());
        node.set("gateway", ONode.load(FinalGateway.config.getRouterConfigs()));

        // pid
        node.set("pid", CommonUtils.getPid());
        return new ResponseResult<>(node);
    }

    @Mapping(value = "/terminalConsole", method = MethodType.GET)
    public Object terminalConsole() {
        ONode node = new ONode();
        node.set("number", WsSimpleListener.terminalMap.size());
        node.set("pool", TerminalHandler.threadPool.getPoolSize());
        return new ResponseResult<>(node);
    }
}
